/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.jms;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Properties;
import java.util.Vector;

import javax.jms.JMSException;
import javax.jms.JMSSecurityException;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
/** 
    This class implements the JMS connection factory. What is being done is, 
    <li> Intitializer class, is called upon to intialize the IP address and 
    the port number to which a connection needs to be made. 
    <li> This Initializer class then instantiates a connection factory, that 
    is responsible for open connections to the particular broker. The 
    getConnection() method requests a TCP socket connection with the broker 
    in question.
    
    @author Shrideep Pallickara
    $Revision$
    $Date$
*/

public class JmsTopicConnectionFactory 
  implements TopicConnectionFactory, JmsDebugFlags  {
  
  /** The hostname of the machine on which the broker process is running */
  private String hostName;
  
  /** The port number on which this broker process is running */
  private int portNum;
  

  /** The port number, from which point on, we look for available free ports
      in increasing order. */
  private int freePortNum=5500;
  
  /** The list of connections maintained by the TopicConnectionFactory */
  private Vector topicConnections;

  /** Indicates the transport type */
  private String transportType;

  private byte transport;

  private Properties connectionProperties;

  private int entityId;
  private String moduleName = "JmsTopicConnectionFactory: ";


  public JmsTopicConnectionFactory(String hostName, int portNum) {
    this.hostName = hostName;
    this.portNum  = portNum;
    
    /** This Topic Connection factory could be used to verify if there is
	a broker process that is running on this host/portNumber.
	The actual initiation of connections is done only after a request
	for connection is made from the ConnectionFactory. 	
    */
    //checkPresenceOfBrokerProcess(hostName, portNum);
    try {
      InetAddress ia = InetAddress.getByName(hostName);
      this.hostName = ia.getHostName();
    }  catch (UnknownHostException exception) {
      if (JmsTopicConnectionFactory_Debug)
	System.out.println(moduleName + "Cannot resolve host => " + hostName + 
			   exception);
    }   
    topicConnections = new Vector();
  }

  public JmsTopicConnectionFactory(String hostName, int portNum,
				   String transportType) {
    this.hostName = hostName;
    this.portNum  = portNum;
    this.transportType = transportType;
    /** This Topic Connection factory could be used to verify if there is
	a broker process that is running on this host/portNumber.
	The actual initiation of connections is done only after a request
	for connection is made from the ConnectionFactory. 	
    */
    //checkPresenceOfBrokerProcess(hostName, portNum);
    try {
      InetAddress ia = InetAddress.getByName(hostName);
      this.hostName = ia.getHostName();
    }  catch (UnknownHostException exception) {
      if (JmsTopicConnectionFactory_Debug)
	System.out.println(moduleName + "Cannot resolve host " + hostName + 
			   exception);
    }   
    topicConnections = new Vector();
  }


  public JmsTopicConnectionFactory(Properties properties,
				   String transportType,
				   int entityId) {
    connectionProperties = properties;
    this.transportType = transportType;
    this.entityId = entityId;
    
    topicConnections = new Vector();
  }

  /** Creates a topic connection with the specified user identity.
    The connection is created in stopped mode. No messages 
    will be delivered until the <code>Connection.start</code> method
    is explicitly called.
    @param userName the caller's user name
    @param password the caller's password
    @return a newly created topic connection
    @exception JMSException if the JMS provider fails to create a topic 
    connection due to some internal error.
    @exception JMSSecurityException if client authentication fails due to 
    an invalid user name or password.
  */ 
  public TopicConnection createTopicConnection(String userName,
					       String password)
    throws JMSException {

    JmsTopicConnection jmsConnection = null;

	 NBJmsInterconnectionBridge nbBridge = initializeNbJmsInterconnectionBridge();
	 jmsConnection = new JmsTopicConnection(nbBridge);

	 topicConnections.addElement(jmsConnection);
	 return jmsConnection;
  }
  
  
  public TopicConnection createTopicConnection() throws JMSException {
    
	  NBJmsInterconnectionBridge nbBridge = initializeNbJmsInterconnectionBridge();
	  JmsTopicConnection jmsConnection = new JmsTopicConnection(nbBridge);
		 
     topicConnections.addElement(jmsConnection);
    
    return jmsConnection;
  }


  private NBJmsInterconnectionBridge initializeNbJmsInterconnectionBridge() 
    throws JMSException {
    NBJmsInterconnectionBridge nbBridge =
      new NBJmsInterconnectionBridge(connectionProperties, transportType,
				     entityId);
    return nbBridge;
  }


  private void checkPresenceOfBrokerProcess(String hostName, int portNum) {
    try {
      InetAddress ia = InetAddress.getByName(hostName);
      hostName = ia.getHostName();
      
      /** Creates a stream socket and connects it to the specified port number
	  at the specified IP address. This is of course a temporary socket */
      Socket temporarySocket = new Socket(ia, portNum);
      
      if (JmsTopicConnectionFactory_Debug)
	System.out.println("Process exists on =>" + hostName+":" + portNum);
      
      temporarySocket.close();
      
    }  catch (UnknownHostException exception) {
      if (JmsTopicConnectionFactory_Debug)
	System.out.println("JmsTopicConnectionFactory:Cannot resolve host " + 
			   exception);
    } catch (IOException ioe) {
      if (JmsTopicConnectionFactory_Debug) {
	System.out.println("The host ((" + hostName + 
			   ")) was resolved, however the connection to "
			   + " port ("+ portNum +") was refused =>" + ioe);
      }
      //ioe.printStackTrace(); 
    }
  }

  private int retrieveAvailablePortForSetup(int freePortNum) {
    ServerSocket tempServerSocket;
    int computedVal=0;
    for (int i= freePortNum; i < 64000 /**max port num allowed */; i++) {
      try {
	tempServerSocket = new ServerSocket(i);
	/** Once we reach here, we can proceed to return this portNum as the
	    port where a process could be created. */
	computedVal = i;
	tempServerSocket.close();
	return computedVal;
      } catch (IOException ioe) {
	/** This means there was a process running on this port, we simply
	    skip it. */
	if (JmsTopicConnectionFactory_Debug) {
	  System.out.println("JmsTopicConnFactory: There exists a process " + 
			     "utilizing TCP port (" +  i + ")");
	}/*end-debug*/
      }/*end-try*/
    } /*end-for*/
    return computedVal;
  }
  
  public static void main(String[] args) {
    if (args.length < 2) {
      System.out.println("Usage: java cgl.narada.jms.JmsTopicConnectionFactory"
			 + " <hostname> <port Number>");
      System.exit(0);
    }/*end-if*/
    
    /** A broker process needs to be up and running at the specified
	hostName:portNum */
    String hostName = args[0];
    int portNum = Integer.parseInt(args[1]);
    JmsTopicConnectionFactory jmsConFac = 
      new JmsTopicConnectionFactory(hostName, portNum);
    try {
      TopicConnection jmsTopicConn = jmsConFac.createTopicConnection();
      TopicSession jmsTopicSession = 
	jmsTopicConn.createTopicSession(true, 0);
      
      TopicSubscriber jmsTopicSub = 
	jmsTopicSession.createDurableSubscriber(new JmsTopic("Scores=NBA, Team=Wizards, Player=Jordan"), "Durable Subs", "Scores=NBA,Team=Wizards,Player=Jordan", true);
      
      TopicPublisher jmsTopicPub =
	jmsTopicSession.createPublisher(new JmsTopic("Scores=NBA, Team = Wizards, Player=Jordan"));
      JmsTextMessage txtMessage = new JmsTextMessage();
      txtMessage.setText("Jordan rules ...");
      txtMessage.setStringProperty("Test", "Property Value being tested");
      txtMessage.setJMSMessageID("Shrideep's Test Header");
      txtMessage.setJMSTimestamp(System.currentTimeMillis());
      txtMessage.setJMSCorrelationID("Wizards-Bulls-Jordan");
      //txtMessage.setJMSType("CoolType");
      txtMessage.setJMSExpiration((long)20);
      txtMessage.setJMSPriority(2);
      jmsTopicPub.publish(txtMessage);
    }catch (JMSException jmsEx) {
      System.out.println(jmsEx);
    }
  }
}











